package mgin

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	pb "github.com/hyperledger/fabric-protos-go/peer"
	"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab"
	"github.com/prometheus/common/log"
	"glucia2a2a1/intermediately"
	"strconv"
)

type HTTP_METHOD string

const (
	POST    HTTP_METHOD = "post"
	GET     HTTP_METHOD = "get"
	ERROR   int         = 201
	SUCCESS int         = 200
)

type MGin struct {
	engine  *gin.Engine
	ledger  *intermediately.Ledger
	channel *intermediately.Channel
	resmgmt *intermediately.ResourceManagement
}

func CreatMGinRocket() *MGin {

	le, err := intermediately.
		NewLedger().
		LoadConfig("./config.yaml").
		LoadContext("./context.json").
		Connect()
	ch, err := intermediately.
		NewChannel().
		LoadConfig("./config.yaml").
		LoadContext("./context.json").
		Connect()
	rm, err := intermediately.
		NewResMgmt().
		LoadConfig("./config.yaml").
		LoadContext("./context.json").
		Connect()

	if err != nil {
		log.Error("block chain client load error")
	}

	return &MGin{
		engine:  gin.Default(),
		ledger:  le,
		channel: ch,
		resmgmt: rm,
	}
}

func (receiver *MGin) LoadingFuel() *MGin {
	// ledger apis
	{
		receiver.addApi("/queryBlockByNumber", POST, receiver.postQueryBlockByNumber())
		receiver.addApi("/queryBlockByHash", POST, receiver.postQueryBlockByHash())
		receiver.addApi("/queryBlockByTxID", POST, receiver.postQueryBlockByTxID())
		receiver.addApi("/queryConfigBlock", POST, receiver.postQueryConfigBlock())
		receiver.addApi("/queryTransaction", POST, receiver.postQueryTransaction())
		receiver.addApi("/queryConfig", POST, receiver.postQueryConfig())
		receiver.addApi("/queryInfo", POST, receiver.postQueryInfo())
	}
	// channel apis
	{
		receiver.addApi("/invoke", POST, receiver.postInvoke())
		receiver.addApi("/query", POST, receiver.postQuery())
	}
	// resmgmt apis
	{
		// query
		receiver.addApi("/queryConfigBlockFromOrderer", POST, receiver.postQueryConfigBlockFromOrderer())
		receiver.addApi("/queryInstantiatedChaincodes", POST, receiver.postQueryInstantiatedChaincodes())
		receiver.addApi("/queryInstalledChaincodes", POST, receiver.postQueryInstalledChaincodes())
		receiver.addApi("/queryCollectionsConfig", POST, receiver.postQueryCollectionsConfig())
		receiver.addApi("/queryConfigFromOrderer", POST, receiver.postQueryConfigFromOrderer())
		receiver.addApi("/queryChannels", POST, receiver.postQueryChannels())
		// lifecycle query
		receiver.addApi("/lifecycleCheckCCCommitReadiness", POST, receiver.postLifecycleCheckCCCommitReadiness())
		receiver.addApi("/lifecycleQueryInstalledCC", POST, receiver.postLifecycleQueryInstalledCC())
		receiver.addApi("/lifecycleQueryCommittedCC", POST, receiver.postLifecycleQueryCommittedCC())
		receiver.addApi("/lifecycleQueryApprovedCC", POST, receiver.postLifecycleQueryApprovedCC())
		// lifecycle install chaincode
		receiver.addApi("/lifecycleChaincodeOnceOk", POST, receiver.postLifecycleChaincodeOnceOk())
	}
	return receiver
}

func (receiver *MGin) RocketLaunching() {
	_ = receiver.engine.Run()
}

/////////////////////// ledger api 蛋壳 /////////////////////// ledger api 蛋壳 ///////////////////////

func (receiver *MGin) postQueryBlockByNumber() gin.HandlerFunc {
	return func(context *gin.Context) {
		height := context.PostForm("number")
		intNum, _ := strconv.Atoi(height)
		result, err := receiver.ledger.QueryBlockByNumber(uint64(intNum))
		if err != nil {
			context.JSON(ERROR, err.Error())
			return
		}
		context.JSON(SUCCESS, result)
	}
}

func (receiver *MGin) postQueryBlockByHash() gin.HandlerFunc {
	// v9pzhhZ4uGQKVTNYaePNrOmmKbebP8iZXjQBnUUi+Pg=
	return func(context *gin.Context) {
		hash := context.PostForm("hash")
		decodeBytes, err := base64.StdEncoding.DecodeString(hash)
		result, err := receiver.ledger.QueryBlockByHash(decodeBytes)
		if err != nil {
			context.JSON(ERROR, err.Error())
			return
		}
		context.JSON(SUCCESS, result)
	}
}

func (receiver *MGin) postQueryBlockByTxID() gin.HandlerFunc {
	// 998a4fd3095052d7b9219ee8c27503c7fcd893e6968f12f2f27655d965232fdf
	return func(context *gin.Context) {
		txId := context.PostForm("txId")
		result, err := receiver.ledger.QueryBlockByTxID(fab.TransactionID(txId))
		if err != nil {
			context.JSON(ERROR, err.Error())
			return
		}
		context.JSON(SUCCESS, result)
	}
}

func (receiver *MGin) postQueryConfigBlock() gin.HandlerFunc {
	return func(context *gin.Context) {
		fabBlockInfo, err := receiver.ledger.QueryConfigBlock()
		if err != nil {
			context.JSON(ERROR, err.Error())
			return
		}
		context.JSON(SUCCESS, fabBlockInfo)

	}
}

func (receiver *MGin) postQueryTransaction() gin.HandlerFunc {
	return func(context *gin.Context) {
		txId := context.PostForm("txId")
		result, err := receiver.ledger.QueryTransaction(fab.TransactionID(txId))
		if err != nil {
			context.JSON(ERROR, err.Error())
			return
		}
		context.JSON(SUCCESS, result)
	}
}

func (receiver *MGin) postQueryConfig() gin.HandlerFunc {
	return func(context *gin.Context) {
		if receiver.ledger.QueryConfig(func(cfg fab.ChannelCfg) {
			c := intermediately.ChannelConfig{
				ID:          cfg.ID(),
				Number:      cfg.BlockNumber(),
				AnchorPeers: cfg.AnchorPeers(),
				MSPs:        cfg.MSPs(),
				Orderers:    cfg.Orderers(),
				Versions:    cfg.Versions(),
			}
			context.JSON(SUCCESS, c)
		}) != nil {
			context.JSON(ERROR, "Query Config ERROR")
		}
	}
}

func (receiver *MGin) postQueryInfo() gin.HandlerFunc {
	return func(context *gin.Context) {
		info, err := receiver.ledger.QueryInfo()
		if err != nil {
			context.JSON(ERROR, err.Error())
			return
		}
		context.JSON(SUCCESS, info)
	}
}

/////////////////////// ledger api 蛋壳 /////////////////////// ledger api 蛋壳 ///////////////////////

func (receiver *MGin) postInvoke() gin.HandlerFunc {
	return func(context *gin.Context) {
		chaincode := context.PostForm("chaincode")
		args := context.PostForm("args")
		fcn := context.PostForm("fcn")
		array := receiver.jsonStrArray2Slice(args)
		resp, err := receiver.channel.Invoke(chaincode, fcn, array...)
		if err != nil {
			context.JSON(ERROR, err.Error())
			return
		}
		context.JSON(SUCCESS, resp)
	}
}

func (receiver *MGin) postQuery() gin.HandlerFunc {
	return func(context *gin.Context) {
		chaincode := context.PostForm("chaincode")
		args := context.PostForm("args")
		fcn := context.PostForm("fcn")
		array := receiver.jsonStrArray2Slice(args)
		resp, err := receiver.channel.Query(chaincode, fcn, array...)
		if err != nil {
			context.JSON(ERROR, err.Error())
			return
		}
		res := intermediately.QueryResult{
			TransactionID: resp.TransactionID,
			Payload:       resp.Payload,
		}
		context.JSON(SUCCESS, res)
	}
}

/////////////////////// ledger api 蛋壳 /////////////////////// ledger api 蛋壳 ///////////////////////

func (receiver *MGin) postQueryChannels() gin.HandlerFunc {
	return func(context *gin.Context) {
		resp, err := receiver.resmgmt.QueryChannels()
		if err != nil {
			context.JSON(ERROR, err.Error())
			return
		}
		context.JSON(SUCCESS, resp)
	}
}

func (receiver *MGin) postQueryConfigBlockFromOrderer() gin.HandlerFunc {
	return func(context *gin.Context) {
		resp, err := receiver.resmgmt.QueryConfigBlockFromOrderer()
		if err != nil {
			context.JSON(ERROR, err.Error())
			return
		}
		context.JSON(SUCCESS, resp)
	}
}

func (receiver *MGin) postQueryInstalledChaincodes() gin.HandlerFunc {
	return func(context *gin.Context) {
		resp, err := receiver.resmgmt.QueryInstantiatedChaincodes()
		if err != nil {
			context.JSON(ERROR, err.Error())
			return
		}
		context.JSON(SUCCESS, resp)
	}
}

func (receiver *MGin) postQueryInstantiatedChaincodes() gin.HandlerFunc {
	return func(context *gin.Context) {
		resp, err := receiver.resmgmt.QueryInstantiatedChaincodes()
		if err != nil {
			context.JSON(ERROR, err.Error())
			return
		}
		context.JSON(SUCCESS, resp)
	}
}

func (receiver *MGin) postQueryCollectionsConfig() gin.HandlerFunc {
	return func(context *gin.Context) {
		fnc := context.PostForm("fnc")
		resp, err := receiver.resmgmt.QueryCollectionsConfig(fnc)
		if err != nil {
			context.JSON(ERROR, err.Error())
			return
		}
		context.JSON(SUCCESS, resp)
	}
}

func (receiver *MGin) postQueryConfigFromOrderer() gin.HandlerFunc {
	return func(context *gin.Context) {
		cfg, err := receiver.resmgmt.QueryConfigFromOrderer()
		if err != nil {
			context.JSON(ERROR, err.Error())
			return
		}
		c := intermediately.ChannelConfig{
			ID:          cfg.ID(),
			Number:      cfg.BlockNumber(),
			AnchorPeers: cfg.AnchorPeers(),
			MSPs:        cfg.MSPs(),
			Orderers:    cfg.Orderers(),
			Versions:    cfg.Versions(),
		}
		context.JSON(SUCCESS, c)
	}
}

func (receiver *MGin) postLifecycleQueryInstalledCC() gin.HandlerFunc {
	return func(context *gin.Context) {
		resp, err := receiver.resmgmt.LifecycleQueryInstalledCC()
		if err != nil {
			context.JSON(ERROR, err.Error())
			return
		}
		context.JSON(SUCCESS, resp)
	}
}

func (receiver *MGin) postLifecycleQueryCommittedCC() gin.HandlerFunc {
	return func(context *gin.Context) {
		nameSpace := context.PostForm("nameSpace")
		resp, err := receiver.resmgmt.LifecycleQueryCommittedCC(nameSpace)
		if err != nil {
			context.JSON(ERROR, err.Error())
			return
		}
		context.JSON(SUCCESS, resp)
	}
}

func (receiver *MGin) postLifecycleQueryApprovedCC() gin.HandlerFunc {
	return func(context *gin.Context) {
		chanicode := context.PostForm("chanicode")
		sequence := context.PostForm("sequence")
		seq, err := strconv.ParseInt(sequence, 10, 64)
		if err != nil {
			context.JSON(ERROR, err.Error())
			return
		}
		peer := context.PostForm("peer")
		resp, err := receiver.resmgmt.LifecycleQueryApprovedCC(chanicode, seq, peer)
		if err != nil {
			context.JSON(ERROR, err.Error())
			return
		}
		context.JSON(SUCCESS, resp)
	}
}

func (receiver *MGin) postLifecycleCheckCCCommitReadiness() gin.HandlerFunc {
	return func(context *gin.Context) {
		chanicode := context.PostForm("chanicode")
		sequence := context.PostForm("sequence")
		seq, err := strconv.ParseInt(sequence, 10, 64)
		if err != nil {
			context.JSON(ERROR, err.Error())
			return
		}
		resp, err := receiver.resmgmt.LifecycleCheckCCCommitReadiness(chanicode, seq)
		if err != nil {
			context.JSON(ERROR, err.Error())
			return
		}
		context.JSON(SUCCESS, resp)
	}
}

func (receiver *MGin) postLifecycleChaincodeOnceOk() gin.HandlerFunc {
	return func(context *gin.Context) {
		signatureMspID := context.PostForm("signatureMspID")
		chaincodePath := context.PostForm("chaincodePath")
		chaincodeName := context.PostForm("chaincodeName")
		label := context.PostForm("label")
		version := context.PostForm("version")
		initFunc := context.PostForm("initFunc")
		isInit := context.PostForm("isInit")
		sequence := context.PostForm("sequence")
		chaincodeType := context.PostForm("chaincodeType")
		args := context.PostForm("args")
		//-----------------------------------------------------
		isInitBool := receiver.str2bool(isInit)
		seq, err := strconv.ParseInt(sequence, 10, 64)
		if err != nil {
			context.JSON(ERROR, err.Error())
			return
		}
		ct, err := strconv.ParseInt(chaincodeType, 10, 32)
		if err != nil {
			context.JSON(ERROR, err.Error())
			return
		}
		array := receiver.jsonStrArray2Slice(args)
		//-----------------------------------------------------
		arrayTxId, resp, err := receiver.resmgmt.LifecycleChaincodeOnceOk(
			receiver.channel,
			signatureMspID,
			chaincodePath,
			chaincodeName,
			label,
			version,
			initFunc,
			isInitBool,
			seq,
			pb.ChaincodeSpec_Type(ct),
			array...,
		)
		if err != nil {
			context.JSON(ERROR, err.Error())
		}
		context.JSON(SUCCESS, intermediately.OnceInstallOkResp{
			TransactionID: arrayTxId,
			Load:          resp,
		})
	}

}

///////////////////////////内部装载工具///////////////////////////内部装载工具///////////////////////////
func (receiver *MGin) addApi(url string, METHOD HTTP_METHOD, handlerFunc gin.HandlerFunc) {
	switch METHOD {
	case POST:
		receiver.engine.POST(url, handlerFunc)
	case GET:
		receiver.engine.GET(url, handlerFunc)
	default:
		break
	}
}

func (receiver *MGin) end() {
	_ = receiver.engine.Run()
}

func (receiver *MGin) jsonStrArray2Slice(jsonArgs string) []string {
	var array []string
	err := json.Unmarshal([]byte(jsonArgs), &array)
	if err != nil {
		fmt.Println(err.Error())
	}
	return array
}

func (receiver *MGin) str2bool(str string) bool {
	switch str {
	case "true":
		return true
	case "false":
		return false
	default:
		return false
	}
}

func (receiver *MGin) name(sequence string) (int64, error) {
	return strconv.ParseInt(sequence, 10, 64)
}
